package com.tcm.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tcm.common.enums.CachePrefix;
import com.tcm.config.AppProperties;
import com.tcm.entity.*;
import com.tcm.entity.bo.UserCacheBo;
import com.tcm.entity.dto.ExercisePublishDto;
import com.tcm.entity.dto.NotificationDto;
import com.tcm.entity.dto.param.ExercisePublishPageParam;
import com.tcm.entity.resp.PageR;
import com.tcm.entity.vo.ExerciseStuResult;
import com.tcm.entity.vo.ExerciseStudentStatusResult;
import com.tcm.exception.DatabaseOperationException;
import com.tcm.exception.EntityInvalidException;
import com.tcm.mapper.ExerciseStudentMapper;
import com.tcm.service.*;
import com.tcm.mapper.ExerciseMapper;
import com.tcm.utils.RedisUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
* @author Guqier
* @description 针对表【ts_exercise(实训练习表)】的数据库操作Service实现
* @createDate 2023-08-10 21:22:50
*/
@Service
public class ExerciseServiceImpl extends ServiceImpl<ExerciseMapper, Exercise> implements ExerciseService{

    @Autowired
    private ExerciseStudentMapper exerciseStudentMapper;

    @Autowired
    private TeacherService teacherService;

    @Autowired
    private TestPaperService testPaperService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ClazzService clazzService;

    @Autowired
    private SocialClazzService socialClazzService;

    @Autowired
    private StudentService studentService;

    @Autowired
    private SocialStudentServiceImpl socialStudentService;

    @Autowired
    private NotificationService notificationService;

    @Autowired
    private UserService userService;

    @Autowired
    private ImageService imageService;

    /**
     * 发布练习
     * @param dto 数据封装
     * @param currUserId 当前登录用户ID
     * @return 处理结果，true为成功
     */
    @Override
    @Transactional
    public boolean publishExercise(ExercisePublishDto dto, Long currUserId){
        // 获取当前用户信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(String.valueOf(currUserId)));
        Teacher teacher = teacherService.getTeacherByAccount(userCacheBo.getAccount());
        if (teacher == null){
            throw new EntityInvalidException("当前用户非教师用户");
        }
        // 构造数据
        TestPaper testPaper = testPaperService.getById(dto.getTestPaperId());
        if (testPaper == null){
            throw new EntityInvalidException("试卷不存在");
        }
        Exercise exercise = new Exercise();
        BeanUtils.copyProperties(dto, exercise, "classIds");
        if (!StringUtils.hasText(dto.getTitle())){// 如果标题缺省，则默认为试卷标题
            exercise.setTitle(testPaper.getTitle());
        }
        exercise.setTeacherId(teacher.getId());
        exercise.setClazzIds(dto.getClassIds().stream().map(String::valueOf).collect(Collectors.joining("|")));
        exercise.setCreateUser(currUserId);
        exercise.setUpdateUser(currUserId);
        if (!this.save(exercise)) {
            throw new DatabaseOperationException("数据库操作失败");
        }
        // 保存实训、学生关系
        if (exercise.getClazzType().equals(0)){
            // 普通班级
            LambdaQueryWrapper<Student> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(Student::getClazzId, dto.getClassIds());
            List<Student> students = studentService.list(queryWrapper);
            int cnt = 0;
            for (Student student : students) {
                ExerciseStudent exerciseStudent = new ExerciseStudent();
                exerciseStudent.setExerciseId(exercise.getId());
                exerciseStudent.setAccount(student.getAccount());
                exerciseStudent.setCompleteStatus(0);// 初始化为未完成
                exerciseStudent.setCreateUser(currUserId);
                exerciseStudent.setUpdateUser(currUserId);
                cnt += exerciseStudentMapper.insert(exerciseStudent);
            }
            if (cnt != students.size()){
                throw new DatabaseOperationException("学生关系保存失败");
            }
        }else {
            // 社会化班级
            LambdaQueryWrapper<SocialStudent> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.in(SocialStudent::getClazzId, dto.getClassIds());
            List<SocialStudent> socialStudents = socialStudentService.list(queryWrapper);
            int cnt = 0;
            for (SocialStudent student : socialStudents) {
                ExerciseStudent exerciseStudent = new ExerciseStudent();
                exerciseStudent.setExerciseId(exercise.getId());
                exerciseStudent.setAccount(student.getAccount());
                exerciseStudent.setCompleteStatus(0);// 初始化为未完成
                exerciseStudent.setCreateUser(currUserId);
                exerciseStudent.setUpdateUser(currUserId);
                cnt += exerciseStudentMapper.insert(exerciseStudent);
            }
            if (cnt != socialStudents.size()){
                throw new DatabaseOperationException("社会化学员关系保存失败");
            }
        }
        // 创建通知消息
        NotificationDto notificationDto = new NotificationDto();
        notificationDto.setTitle(String.format("您的教师【%s】发布了新练习", teacher.getName()));
        notificationDto.setContent(String.format("您的教师【%s】练习【%s】截止时间为【%s】，请尽快完成！", teacher.getName(), exercise.getTitle(), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(exercise.getDisableTime())));
        notificationDto.setType(2);// 系统通知
        notificationDto.setIsRoll(0);
        notificationDto.setClazzType(exercise.getClazzType());
        notificationDto.setClazzIds(dto.getClassIds());
        if (!notificationService.addNotification(notificationDto, currUserId)) {
            throw new DatabaseOperationException("创建通知失败");
        }
        return true;
    }

    /**
     * 取消发布练习
     * @param id 练习ID
     * @return 处理结果，true为成功，false为练习不存在
     */
    @Override
    @Transactional
    public boolean canselPublishExercise(Long id){
        // 删除练习实体类
        Exercise exercise = this.getById(id);
        if (exercise == null) {
            return false;
        }
        boolean b = this.removeById(id);
        // 删除练习、学生关系
        LambdaQueryWrapper<ExerciseStudent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ExerciseStudent::getExerciseId, exercise.getId());
        Integer count = exerciseStudentMapper.selectCount(queryWrapper);
        int delete = exerciseStudentMapper.delete(queryWrapper);

        if (!b || count != delete){
            throw new DatabaseOperationException("数据库操作失败");
        }
        return true;
    }

    /**
     * 教师用户分页获取发布练习列表
     * @param param 分页参数
     * @return 发布练习列表
     */
    @Override
    public PageR<ExercisePublishDto> getPublishExerciseListByPage(ExercisePublishPageParam param, Long currUserId){
        // 获取当前用户信息
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(String.valueOf(currUserId)));
        Teacher teacher = teacherService.getTeacherByAccount(userCacheBo.getAccount());
        if (teacher == null){
            throw new EntityInvalidException("当前用户非教师用户");
        }
        // 构造查询条件
        Page<Exercise> page = new Page<>(param.getPage(), param.getPageSize());
        page.addOrder(OrderItem.desc("update_time"));
        LambdaQueryWrapper<Exercise> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Exercise::getTeacherId, teacher.getId());
        queryWrapper.like(StringUtils.hasText(param.getTitle()), Exercise::getTitle, param.getTitle());
        // 获取数据
        this.page(page, queryWrapper);
        List<Exercise> exerciseList = page.getRecords();
        // 准备数据
        List<Clazz> clazzList = clazzService.list();
        List<SocialClazz> socialClazzList = socialClazzService.list();
        List<ExerciseStudent> exerciseStudents = exerciseStudentMapper.selectList(null);
        // 构造结果集
        PageR<ExercisePublishDto> pageR = new PageR<>();
        BeanUtils.copyProperties(page, pageR, "records");
        pageR.setRecords(exerciseList.stream().map(exercise -> {
            ExercisePublishDto dto = new ExercisePublishDto();
            BeanUtils.copyProperties(exercise, dto);
            dto.setClassIds(Arrays.stream(exercise.getClazzIds().split("\\|")).map(Long::parseLong).collect(Collectors.toList()));
            // 判断是否是社会化班级
            if (exercise.getClazzType().equals(0)) {
                dto.setClazzNames(clazzList.stream().filter(clazz -> dto.getClassIds().contains(clazz.getId())).map(Clazz::getName).collect(Collectors.toList()));
            }else {
                dto.setClazzNames(socialClazzList.stream().filter(socialClazz -> dto.getClassIds().contains(socialClazz.getId())).map(SocialClazz::getName).collect(Collectors.toList()));
            }
            // 查询人数
            List<ExerciseStudent> exerciseStudentList = exerciseStudents
                    .stream()
                    .filter(exerciseStudent -> exerciseStudent.getExerciseId().equals(exercise.getId()))
                    .collect(Collectors.toList());
            dto.setTotalNum(exerciseStudentList.size());
            dto.setCompletedNum(new Long(exerciseStudentList.stream().filter(exerciseStudent -> exerciseStudent.getCompleteStatus().equals(1)).count()).intValue());
            return dto;
        }).collect(Collectors.toList()));
        return pageR;
    }

    /**
     * 学生获取已发布练习
     * @param currUserId 当前登录用户ID
     * @return 当前学生的已发布练习
     */
    @Override
    public List<ExerciseStuResult> getStuExerciseList(Long currUserId){
        // 查询符合条件的练习
        UserCacheBo userCacheBo = redisUtil.getCacheObject(CachePrefix.PC_USER_TOKEN.getCacheKey(currUserId.toString()));
        LambdaQueryWrapper<ExerciseStudent> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ExerciseStudent::getAccount, userCacheBo.getAccount());
        queryWrapper.orderByDesc(ExerciseStudent::getUpdateTime);
        List<ExerciseStudent> exerciseStudents = exerciseStudentMapper.selectList(queryWrapper);

        // 构造结果集
        List<ExerciseStuResult> results = new ArrayList<>();
        if (!CollectionUtils.isEmpty(exerciseStudents)) {
            // 准备数据
            List<Exercise> exercises = this.listByIds(exerciseStudents.stream().map(ExerciseStudent::getExerciseId).distinct().collect(Collectors.toList()));
            List<Teacher> teacherList = teacherService.list();
            // 构造结果
            results.addAll( exerciseStudents.stream().map(exerciseStudent -> {
                ExerciseStuResult result = new ExerciseStuResult();
                result.setCompleteStatus(exerciseStudent.getCompleteStatus());
                exercises.stream().filter(exercise -> exercise.getId().equals(exerciseStudent.getExerciseId())).findFirst().ifPresent(exercise -> {
                    BeanUtils.copyProperties(exercise, result);
                    // 匹配教师姓名
                    teacherList.stream().filter(teacher -> teacher.getId().equals(result.getTeacherId())).findFirst().ifPresent(teacher -> result.setTeacherName(teacher.getName()));
                });
                return result;
            }).collect(Collectors.toList()));
        }
        return results;
    }

    /**
     * 获取学生练习情况列表
     * @param id 练习ID
     * @return 学生练习情况列表
     */
    @Override
    public List<ExerciseStudentStatusResult> getStudentCompletionStatusList(Long id){
        List<ExerciseStudent> exerciseStudents = exerciseStudentMapper.selectList(new LambdaQueryWrapper<ExerciseStudent>()
                .eq(ExerciseStudent::getExerciseId, id)
                .orderByAsc(ExerciseStudent::getCompleteStatus));// 按照完成情况排序，未完成的靠上
        return exerciseStudents.stream().map(exerciseStudent -> {
            ExerciseStudentStatusResult result = new ExerciseStudentStatusResult();
            User user = userService.getUserByAccount(exerciseStudent.getAccount());
            result.setName(user.getName());
            if (user.getAvatar() != null) {
                result.setAvatarUrl(imageService.getImageById(user.getAvatar()).getRemoteFileUrl());
            }
            result.setCompleteStatus(exerciseStudent.getCompleteStatus());
            if (user.getUserType().equals(AppProperties.STUDENT_USER_TYPE)) {
                Student studentByAccount = studentService.getStudentByAccount(exerciseStudent.getAccount());
                Clazz clazz = clazzService.getById(studentByAccount.getClazzId());
                result.setClazzType(0);
                result.setClazzName(clazz.getName());
            } else if (user.getUserType().equals(AppProperties.SOCIAL_STUDENT_USER_TYPE)) {
                SocialStudent socialStudent = socialStudentService.getSocialStudentByAccount(exerciseStudent.getAccount());
                SocialClazz socialClazz = socialClazzService.getById(socialStudent.getClazzId());
                result.setClazzType(1);
                result.setClazzName(socialClazz.getName());
            }
            return result;
        }).collect(Collectors.toList());
    }

    /**
     * 获取练习学生关系
     * @param exerciseId 练习ID
     * @param account 学生账号
     * @return 关系对象
     */
    @Override
    public ExerciseStudent getExerciseStudent(Long exerciseId, String account){
        return exerciseStudentMapper.selectOne(new LambdaQueryWrapper<ExerciseStudent>().eq(ExerciseStudent::getExerciseId, exerciseId).eq(ExerciseStudent::getAccount, account));
    }

    /**
     * 标记完成练习
     * @param exerciseId 练习ID
     * @param account 学生账号
     * @return 处理结果
     */
    @Override
    public boolean completeExercise(Long exerciseId, String account){
        ExerciseStudent exerciseStudent = exerciseStudentMapper.selectOne(new LambdaQueryWrapper<ExerciseStudent>().eq(ExerciseStudent::getExerciseId, exerciseId).eq(ExerciseStudent::getAccount, account));
        if (Objects.isNull(exerciseStudent)) {
            return false;
        }
        exerciseStudent.setCompleteStatus(1);
        return exerciseStudentMapper.updateById(exerciseStudent) == 1;
    }
}




